package com.canyou.operlog;

import cn.hutool.core.util.StrUtil;
import com.canyou.api.ResponseBO;
import com.canyou.enums.ErrorCodeEnum;
import com.canyou.utils.IpUtils;
import com.canyou.utils.JsonUtils;
import com.canyou.utils.Oauth2Utils;
import com.canyou.utils.StringUtils;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

import static com.canyou.api.ResponseBO.ok;

/**
 * 操作日志记录处理，开启异步调用
 *
 * @author fuyong
 */
@Aspect
@Component
@Order(value = 3)
@Slf4j
public class OpLogAspect {

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 配置织入点
     */
    @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
    public void logPointCut() {
    }

    @Before("logPointCut()")
    public void beforeMethod(JoinPoint joinPoint) {
        OpLogUtils.startStopWatch();
    }

    /**
     * 前置通知 用于拦截操作
     *
     * @param joinPoint 切点
     */
    @AfterReturning(returning = "result", pointcut = "logPointCut()")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, result, null);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, null, e);
    }

    private void handleLog(final JoinPoint joinPoint, Object result, Exception throwable) {
        long spendTime = OpLogUtils.getStopWatchTime();
        OpLogUtils.OpLogEvent operateLog = OpLogUtils.getLog();
        if (operateLog == null) {
            return;
        }
        if (throwable != null) {
            return;
        }
        // 有操作记录内容才记录操作日志
        if (operateLog.getOperateList() == null || operateLog.getOperateList().isEmpty()) {
            return;
        }

        ResponseBO res = ok();
        // 判断返回值是否是ResponseBO
        if (result instanceof ResponseBO) {
            // 判断返回值是否是成功返回, code == 2000
            res = (ResponseBO) result;
            if (!ErrorCodeEnum.SUCCESS.getCode().equals(res.getCode())) {
                return;
            }
        }

        // 获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        if (attributes.getResponse() != null && attributes.getResponse().getStatus() != HttpStatus.OK.value()) {
            return;
        }

        String method = request.getMethod();
        String userName = Oauth2Utils.getUserName();
        String clientId = Oauth2Utils.getClientId();
        try {
            String module = StrUtil.EMPTY;
            // 获得注解
            Api classLog = getClassAnnotationLog(joinPoint);
            if (classLog != null) {
                module = String.join(",", classLog.tags());
            }
            Object[] obj = JsonUtils.transFile(joinPoint.getArgs());
            String params = JsonUtils.toString(obj);
            operateLog.setRequestParam(params)
                    .setRequestType(method)
                    .setUsername(userName)
                    .setClientId(clientId)
                    .setIpAddr(IpUtils.getIpAddr(request))
                    .setRequestUrl(request.getRequestURI())
                    .setResponseCode(res.getCode())
                    .setResponseData(JsonUtils.toString(res.getData()))
                    .setResponseMsg(res.getMsg())
                    .setSpendTime(spendTime);
            if (StringUtils.isBlank(operateLog.getModule())) {
                operateLog.setModule(module);
            }
            applicationContext.publishEvent(operateLog);
        } catch (Exception ex) {
            log.error("记录操作日志异常", ex);
        }
    }

    /**
     * 类是否存在注解，如果存在就获取
     */
    private Api getClassAnnotationLog(JoinPoint joinPoint) {
        return joinPoint.getTarget().getClass().getAnnotation(Api.class);
    }

}
